package geo.google.mapping;

import com.magenta.maxoptra.gis.GeoException;
import com.magenta.mrs.geocoding.mapping.Functor;
import geo.google.datamodel.GeoAddress;
import geo.google.datamodel.GeoUsAddress;
import oasis.names.tc.ciq.xsdschema.xal._2.AddressDetails.Country;
import oasis.names.tc.ciq.xsdschema.xal._2.AddressDetails.Country.CountryNameCode;
import oasis.names.tc.ciq.xsdschema.xal._2.AdministrativeArea;
import oasis.names.tc.ciq.xsdschema.xal._2.AdministrativeArea.AdministrativeAreaName;
import oasis.names.tc.ciq.xsdschema.xal._2.AdministrativeArea.SubAdministrativeArea;
import oasis.names.tc.ciq.xsdschema.xal._2.AdministrativeArea.SubAdministrativeArea.SubAdministrativeAreaName;
import oasis.names.tc.ciq.xsdschema.xal._2.Locality;
import oasis.names.tc.ciq.xsdschema.xal._2.Locality.LocalityName;
import oasis.names.tc.ciq.xsdschema.xal._2.PostalCode;
import oasis.names.tc.ciq.xsdschema.xal._2.PostalCode.PostalCodeNumber;
import oasis.names.tc.ciq.xsdschema.xal._2.*;

import java.util.List;

/**
 * Mapping function that maps a {@link GeoAddress} object to a {@link GeoUsAddress} object.
 *
 * @author jliang
 */
public final class AddressToUsAddressFunctor implements Functor<GeoUsAddress, GeoAddress, GeoException> {
    private static final AddressToUsAddressFunctor _instance = new AddressToUsAddressFunctor();

    public static AddressToUsAddressFunctor getInstance() {
        return _instance;
    }

    public GeoUsAddress execute(GeoAddress addr) {
        GeoUsAddress ret = new GeoUsAddress();
        Country country = addr.getAddressDetails().getCountry();
        if (country != null) {
            //country
            List<CountryNameCode> countryNames = country.getCountryNameCode();
            if (!countryNames.isEmpty()) {
                ret.setCountry(countryNames.get(0).getContent());
            } else {
                List<CountryName> countryFullNames = country.getCountryName();
                if (!countryFullNames.isEmpty()) {
                    ret.setCountry(countryFullNames.get(0).getContent());
                }
            }

            AdministrativeArea adminArea = country.getAdministrativeArea();
            Locality locality = null;
            PostalCode postalCode = null;
            Thoroughfare thoroughfare = country.getThoroughfare();
            if (addr.getAddressDetails().getLocality() != null) {
                locality = addr.getAddressDetails().getLocality();
            }
            if (adminArea != null) {
                //  state
                List<AdministrativeAreaName> names = adminArea.getAdministrativeAreaName();
                if (!names.isEmpty()) {
                    ret.setState(names.get(0).getContent());
                    ret.setCounty(names.get(0).getContent());
                }
                if (adminArea.getPostalCode() != null) {
                    postalCode = adminArea.getPostalCode();
                }
                if (adminArea.getLocality() != null) {
                    locality = adminArea.getLocality();
                }
                SubAdministrativeArea subAdminArea = adminArea.getSubAdministrativeArea();
                if (subAdminArea != null) {
                    if (subAdminArea.getLocality() != null) {
                        locality = subAdminArea.getLocality();
                    }
                    //district
                    List<SubAdministrativeAreaName> subNames = subAdminArea.getSubAdministrativeAreaName();
                    if (!subNames.isEmpty()) {
                        ret.setDistrict(subNames.get(0).getContent());
                    }
                    // street
                    List<Object> any = subAdminArea.getAny();
                    if (!any.isEmpty()) {
                        for (final Object obj: any) {
                            if (obj instanceof Thoroughfare) {
                                thoroughfare = (Thoroughfare) obj;
                            }
                        }
                    }
                    if (subAdminArea.getPostalCode() != null) {
                        postalCode = subAdminArea.getPostalCode();
                    }
                }
            } else {
                if (country.getLocality() != null) {
                    locality = country.getLocality();
                }
            }
            if (locality != null) {
                List<LocalityName> localNames = locality.getLocalityName();
                if (!localNames.isEmpty()) {
                    ret.setCity(localNames.get(0).getContent());
                }

                //postal code
                if (locality.getPostalCode() != null) {
                    postalCode = locality.getPostalCode();
                }
                 if (locality.getThoroughfare() != null) {
                    thoroughfare = locality.getThoroughfare();
                }
                DependentLocalityType dependentLocality = locality.getDependentLocality();
                if (dependentLocality != null) {
                    if (ret.getCity() == null) {
                        List<DependentLocalityType.DependentLocalityName> dependLocalNames = dependentLocality.getDependentLocalityName();
                        if (!dependLocalNames.isEmpty()) {
                            ret.setCity(localNames.get(0).getContent());
                        }
                    }
                    if (ret.getPostalCode() == null || ret.getPostalCode().isEmpty()) {
                        postalCode = dependentLocality.getPostalCode();
                    }
                    if (thoroughfare == null) {
                        thoroughfare = dependentLocality.getThoroughfare();
                    }
                }
            }
            if (postalCode != null) {
                List<PostalCodeNumber> numbers = postalCode.getPostalCodeNumber();
                if (!numbers.isEmpty()) {
                    ret.setPostalCode(numbers.get(0).getContent());
                }
            }
            if (thoroughfare != null) {
                //addresslines
                List<ThoroughfareNameType> thoroughfares = thoroughfare.getThoroughfareName();
                if (!thoroughfares.isEmpty()) {
                    ret.setAddressLine1(thoroughfares.get(0).getContent());
                    if (thoroughfares.size() > 1) {
                        ret.setAddressLine2(thoroughfares.get(1).getContent());
                    }
                }
            }
        }

        if (ret.getCity().equals(ret.getDistrict())) {
            ret.setDistrict(null);
        }
        //need to clone
        if (addr.getCoordinate() != null) {
            ret.setCoordinate(addr.getCoordinate().clone());
        }
        ret.setAccuracy(addr.getAccuracy());
        return ret;
    }
}
